/**
 @file  protocol.h
 @brief ENet protocol
*/
#ifndef __ENET_PROTOCOL_H__
#define __ENET_PROTOCOL_H__

#include "enet/types.h"

enum
{
	ENET_PROTOCOL_MINIMUM_MTU = 576,
	ENET_PROTOCOL_MAXIMUM_MTU = 4096,
	ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32,
	ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096,
	ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 32768,
	ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1,
	ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255,
	ENET_PROTOCOL_MAXIMUM_PEER_ID = 0x7F
};

typedef enum _ENetProtocolCommand
{
	ENET_PROTOCOL_COMMAND_NONE = 0,
	ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1,
	ENET_PROTOCOL_COMMAND_CONNECT = 2,
	ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3,
	ENET_PROTOCOL_COMMAND_DISCONNECT = 4,
	ENET_PROTOCOL_COMMAND_PING = 5,
	ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6,
	ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7,
	ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8,
	ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9,
	ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10,
	ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11,
	ENET_PROTOCOL_COMMAND_COUNT = 12,

	ENET_PROTOCOL_COMMAND_MASK = 0x0F
} ENetProtocolCommand;

typedef enum _ENetProtocolFlag
{
	ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7),
	ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6),

	ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 7),
	ENET_PROTOCOL_HEADER_FLAG_MASK = 0x80
} ENetProtocolFlag;

#ifdef _MSC_VER_
#pragma pack(push, 1)
#define ENET_PACKED
#elif defined(__GNUC__)
#define ENET_PACKED __attribute__ ((packed))
#else
#define ENET_PACKED
#endif

typedef struct _ENetProtocolHeader
{
	enet_uint32 unk_prefix;
	enet_uint8 unk;
	enet_uint8 peerID;
	enet_uint16 sentTime;
} ENET_PACKED ENetProtocolHeader;

typedef struct _ENetProtocolCommandHeader
{
	enet_uint8 command;
	enet_uint8 channelID;
	enet_uint16 reliableSequenceNumber;
} ENET_PACKED ENetProtocolCommandHeader;

typedef struct _ENetProtocolAcknowledge
{
	ENetProtocolCommandHeader header;
	enet_uint16 receivedReliableSequenceNumber;
	enet_uint16 receivedSentTime;
} ENET_PACKED ENetProtocolAcknowledge;

typedef struct _ENetProtocolConnect
{
	ENetProtocolCommandHeader header;
	enet_uint8 outgoingPeerID;
	enet_uint16 mtu;
	enet_uint32 windowSize;
	enet_uint32 channelCount;
	enet_uint32 incomingBandwidth;
	enet_uint32 outgoingBandwidth;
	enet_uint32 packetThrottleInterval;
	enet_uint32 packetThrottleAcceleration;
	enet_uint32 packetThrottleDeceleration;
	enet_uint32 sessionID;
} ENET_PACKED ENetProtocolConnect;

typedef struct _ENetProtocolVerifyConnect
{
	ENetProtocolCommandHeader header;
	enet_uint8 outgoingPeerID;
	enet_uint16 mtu;
	enet_uint32 windowSize;
	enet_uint32 channelCount;
	enet_uint32 incomingBandwidth;
	enet_uint32 outgoingBandwidth;
	enet_uint32 packetThrottleInterval;
	enet_uint32 packetThrottleAcceleration;
	enet_uint32 packetThrottleDeceleration;
} ENET_PACKED ENetProtocolVerifyConnect;

typedef struct _ENetProtocolBandwidthLimit
{
	ENetProtocolCommandHeader header;
	enet_uint32 incomingBandwidth;
	enet_uint32 outgoingBandwidth;
} ENET_PACKED ENetProtocolBandwidthLimit;

typedef struct _ENetProtocolThrottleConfigure
{
	ENetProtocolCommandHeader header;
	enet_uint32 packetThrottleInterval;
	enet_uint32 packetThrottleAcceleration;
	enet_uint32 packetThrottleDeceleration;
} ENET_PACKED ENetProtocolThrottleConfigure;

typedef struct _ENetProtocolDisconnect
{
	ENetProtocolCommandHeader header;
	enet_uint32 data;
} ENET_PACKED ENetProtocolDisconnect;

typedef struct _ENetProtocolPing
{
	ENetProtocolCommandHeader header;
} ENET_PACKED ENetProtocolPing;

typedef struct _ENetProtocolSendReliable
{
	ENetProtocolCommandHeader header;
	enet_uint16 dataLength;
} ENET_PACKED ENetProtocolSendReliable;

typedef struct _ENetProtocolSendUnreliable
{
	ENetProtocolCommandHeader header;
	enet_uint16 unreliableSequenceNumber;
	enet_uint16 dataLength;
} ENET_PACKED ENetProtocolSendUnreliable;

typedef struct _ENetProtocolSendUnsequenced
{
	ENetProtocolCommandHeader header;
	enet_uint16 unsequencedGroup;
	enet_uint16 dataLength;
} ENET_PACKED ENetProtocolSendUnsequenced;

typedef struct _ENetProtocolSendFragment
{
	ENetProtocolCommandHeader header;
	enet_uint16 startSequenceNumber;
	enet_uint16 dataLength;
	enet_uint32 fragmentCount;
	enet_uint32 fragmentNumber;
	enet_uint32 totalLength;
	enet_uint32 fragmentOffset;
} ENET_PACKED ENetProtocolSendFragment;

typedef union _ENetProtocol
{
	ENetProtocolCommandHeader header;
	ENetProtocolAcknowledge acknowledge;
	ENetProtocolConnect connect;
	ENetProtocolVerifyConnect verifyConnect;
	ENetProtocolDisconnect disconnect;
	ENetProtocolPing ping;
	ENetProtocolSendReliable sendReliable;
	ENetProtocolSendUnreliable sendUnreliable;
	ENetProtocolSendUnsequenced sendUnsequenced;
	ENetProtocolSendFragment sendFragment;
	ENetProtocolBandwidthLimit bandwidthLimit;
	ENetProtocolThrottleConfigure throttleConfigure;
} ENET_PACKED ENetProtocol;

#ifdef _MSC_VER_
#pragma pack(pop)
#endif

#endif /* __ENET_PROTOCOL_H__ */
